package com.clear.auth.domain.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.clear.auth.common.enums.AuthUserStatusEnum;
import com.clear.auth.common.enums.IsDeletedFlagEnum;
import com.clear.common.constant.AuthConstants;
import com.clear.auth.domain.convert.AuthUserConverter;
import com.clear.auth.domain.entity.AuthUserBO;
import com.clear.auth.domain.redis.RedisUtil;
import com.clear.auth.domain.service.AuthUserDomainService;
import com.clear.auth.infra.basic.entity.*;
import com.clear.auth.infra.basic.service.*;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;


@Service
@Slf4j
public class AuthUserDomainServiceImpl implements AuthUserDomainService {

    @Resource
    private AuthUserService authUserService;

    @Resource
    private AuthRoleService authRoleService;

    @Resource
    private AuthUserRoleService authUserRoleService;

    @Resource
    private AuthPermissionService authPermissionService;

    @Resource
    private AuthRolePermissionService authRolePermissionService;

    @Resource
    private RedisUtil redisUtil;

    private final static String SALT = "clear666";
    private final static String USER_DEFAULT_PASSWORD = "123456";
    private final static String USER_DEFAULT_NAME = "小黑子";

    private static final String LOGIN_PREFIX = "loginCode";

    /**
     * 用户注册
     *
     * @param authUserBO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainServiceImpl.register.bo:{}", JSON.toJSONString(authUserBO));
        }
        // 查数据库，校验用户是否已存在，不存在才注册
        AuthUser existsAuthUser = new AuthUser();
        existsAuthUser.setUserName(authUserBO.getUserName());  // openId
        AuthUser resultAuthUser = authUserService.queryByCondition(existsAuthUser);
        if (resultAuthUser != null) {
            return true;
        }

        // 用户不存在才执行下面用户注册逻辑
        AuthUser authUser = AuthUserConverter.INSTANCE.convertBOToEntity(authUserBO);

        if (StringUtils.isBlank(authUser.getPassword())) {  // 避免微信登录时密码为空引起的异常
            // 为用户设置初始密码
            authUser.setPassword(SaSecureUtil.md5BySalt(USER_DEFAULT_PASSWORD, SALT));
        } else {
            // 用户密码加密
            authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(), SALT));
        }
        // 为用户设置默认头像
        if (StringUtils.isBlank(authUser.getAvatar())) {
            authUser.setAvatar("http://192.168.188.150:9000/jichi/icon/0040f601ccd7710f259d3778965d24b4.png");
        }
        // 为用户设置默认昵称
        if (StringUtils.isBlank(authUser.getNickName())) {
            authUser.setNickName(USER_DEFAULT_NAME);
        }
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        int count = authUserService.insert(authUser);

        // =============建立一个初步的角色的关联==================
        // 1）获取role_id
        AuthRole authRole = new AuthRole();
        authRole.setRoleKey(AuthConstants.NORMAL_USER); // 初始给用户设置普通权限
        AuthRole authRoleResult = authRoleService.queryByCondition(authRole);
        Long roleId = authRoleResult.getId();
        // 2）获取user_id
        Long userId = authUser.getId();
        // 3）建立关联
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setUserId(userId);
        authUserRole.setRoleId(roleId);
        authUserRole.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        authUserRoleService.insert(authUserRole);

        //===========要把当前用户的角色和权限都刷到我们的redis里===========
        //  ======用户角色写入缓存==========
        String roleKey = redisUtil.buildKey(AuthConstants.AUTH_ROLE_PREFIX, authUser.getUserName());
        List<AuthRole> roleList = new LinkedList<>();
        roleList.add(authRole);
        redisUtil.set(roleKey, new Gson().toJson(roleList));

        //  ======用户权限写入缓存==========
        // 1)根据角色id去 角色权限关系表 中查所有的权限id
        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(roleId);
        List<AuthRolePermission> rolePermissionList = authRolePermissionService.queryByCondition(authRolePermission);

        List<Long> permissionIdList = rolePermissionList.stream()
                .map(AuthRolePermission::getPermissionId).collect(Collectors.toList());

        // 2)根据 权限id列表 去 权限表 中查所有的权限
        List<AuthPermission> permissionList = authPermissionService.queryByPermissionIdList(permissionIdList);
        // 3)存入缓存
        String permissionKey = redisUtil.buildKey(AuthConstants.AUTH_PERMISSION_PREFIX, authUser.getUserName());
        redisUtil.set(permissionKey, new Gson().toJson(permissionList));

        return count > 0;
    }

    /**
     * 更新用户信息
     *
     * @param authUserBO
     * @return
     */
    @Override
    public boolean update(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainServiceImpl.update.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserConverter.INSTANCE.convertBOToEntity(authUserBO);

        int count = authUserService.updateByUserName(authUser);

        // 有任何的更新，都要与缓缓进行同步的修改

        return count > 0;
    }

    /**
     * 逻辑删除
     *
     * @param authUserBO
     * @return
     */
    @Override
    public boolean delete(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainServiceImpl.delete.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserConverter.INSTANCE.convertBOToEntity(authUserBO);
        // 逻辑删除
        authUser.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());

        int count = authUserService.update(authUser);

        // 有任何的更新，都要与缓缓进行同步的修改

        return count > 0;
    }

    /**
     * 用户启用/禁用
     *
     * @param authUserBO
     * @return
     */
    @Override
    public boolean changeStatus(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainServiceImpl.changeStatus.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserConverter.INSTANCE.convertBOToEntity(authUserBO);

        // 变更用户状态
        int count = authUserService.update(authUser);

        // 有任何的更新，都要与缓缓进行同步的修改

        return count > 0;
    }

    /**
     * 用户登录
     * 从缓存中获取loginId，为空则登录失败。登录成功，删除缓存中的验证码
     *
     * @param verificationCode 验证码
     * @return
     */
    @Override
    public SaTokenInfo doLogin(String verificationCode) {
        log.info("AuthUserDomainServiceImpl.doLogin.param.verificationCode:{}", verificationCode);

        String loginKey = redisUtil.buildKey(LOGIN_PREFIX, verificationCode);
        // 获取缓存（loginCode.验证码 openId     拿到openId）
        String openId = redisUtil.get(loginKey);
        if (StringUtils.isBlank(openId)) {
            return null;
        }
        // =====================用户第一次登录：注册用户=====================
        // 注册
        AuthUserBO authUserBO = new AuthUserBO();
        authUserBO.setUserName(openId);
        this.register(authUserBO);  // 如果不是第一次登录，这个方法将无效：该方法进行了判断
        // 登录
        // 会话登录：参数填写要登录的账号id，建议的数据类型：long | int | String， 不可以传入复杂类型，如：User、Admin 等等
        StpUtil.login(openId);
        // todo 登录成功后，将用户权限信息、角色信息缓存到redis中
        cacheUserRoleAndPermissions(openId);

        // 获取当前会话的 token 信息参数
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        // todo 删除缓存中的验证码
        //redisUtil.del(loginKey);
        return tokenInfo;
    }

    /**
     * 缓存用户角色、权限信息
     *
     * @param openId openId
     */
    private void cacheUserRoleAndPermissions(String openId) {
        // ======用户角色写入缓存==========
        // 1.根据openId去auth_user表查询用户信息
        AuthUser authUser = new AuthUser();
        authUser.setUserName(openId);
        AuthUser resultAuthUser = authUserService.queryByCondition(authUser);
        // 2.根据用户id去auth_user_role表查询角色信息
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setUserId(resultAuthUser.getId());
        AuthUserRole resultAuthUserRole = authUserRoleService.queryByCondition(authUserRole);
        Long roleId = resultAuthUserRole.getRoleId();
        // 3.根据角色id去auth_role表查询角色信息
        AuthRole authRole = authRoleService.queryById(roleId);
        // 4.写入缓存
        List<AuthRole> roleList = new LinkedList<>();
        roleList.add(authRole);
        // a.构建缓存key
        String roleKey = redisUtil.buildKey(AuthConstants.AUTH_ROLE_PREFIX, openId);
        // b.存入缓存
        redisUtil.set(roleKey, new Gson().toJson(roleList));


        //  ======用户权限写入缓存==========
        // 1.根据角色id去 auth_role_permission表中查所有的权限id
        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(roleId);
        List<AuthRolePermission> rolePermissionList = authRolePermissionService.queryByCondition(authRolePermission);
        List<Long> permissionIdList = rolePermissionList.stream()
                .map(AuthRolePermission::getPermissionId).collect(Collectors.toList());
        // 2.根据 权限id列表 去 auth_permission表 中查所有的权限
        List<AuthPermission> permissionList = authPermissionService.queryByPermissionIdList(permissionIdList);
        // 3.写入缓存
        // a.构建缓存key
        String permissionKey = redisUtil.buildKey(AuthConstants.AUTH_PERMISSION_PREFIX, openId);
        // b.存入缓存
        redisUtil.set(permissionKey, new Gson().toJson(permissionList));
    }

    /**
     * 查看用户信息
     *
     * @param authUserBO
     * @return
     */
    @Override
    public AuthUserBO getUserInfo(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainServiceImpl.getUserInfo.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserConverter.INSTANCE.convertBOToEntity(authUserBO);

        AuthUser userInfo = authUserService.queryByCondition(authUser);
        if (Objects.isNull(userInfo)) {
            return new AuthUserBO();
        }
        AuthUserBO userInfoBO = AuthUserConverter.INSTANCE.convertEntityToBO(userInfo);
        return userInfoBO;
    }
}
